package util {

import automation.SeleFxPropertyDescriptor;
import codec.*;

import flash.display.DisplayObject;
import flash.events.*;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.display.DisplayObject;
import flash.utils.getDefinitionByName;
import flash.system.Capabilities;

import mx.events.FlexEvent;
import mx.automation.Automation;
import mx.automation.AutomationError;
import mx.automation.AutomationID;
import mx.automation.AutomationIDPart;
import mx.automation.IAutomationClass;
import mx.automation.IAutomationManager;
import mx.automation.IAutomationObject;
import mx.automation.IAutomationTabularData;
import mx.automation.IAutomationMethodDescriptor;
import mx.automation.IAutomationEventDescriptor;
import mx.automation.IAutomationPropertyDescriptor;


[Mixin]
public class SeleFxCodecHelper {

    private static var _root:DisplayObject;
    private static var selefxCodecHelper:SeleFxCodecHelper;
    private static var isInitailized:Boolean = false;

    private var propertyCodecMap:Object = [];

    public static function init(root:DisplayObject):void {
        if (selefxCodecHelper == null) {
            _root = root;
            _root.addEventListener(FlexEvent.APPLICATION_COMPLETE, applicationCompleteHandler);
        }
    }

    private static function applicationCompleteHandler(event:FlexEvent):void {
        _root.removeEventListener(FlexEvent.APPLICATION_COMPLETE, applicationCompleteHandler);
        selefxCodecHelper = new SeleFxCodecHelper();
    }

    public static function getCodecHelper():SeleFxCodecHelper {
        return selefxCodecHelper;
    }

    public function SeleFxCodecHelper() {
        super();

        if (! isInitailized) {
            addPropertyCodec("object", new DefaultPropertyCodec());
            addPropertyCodec("keyCode", new KeyCodePropertyCodec());
            addPropertyCodec("keyModifier", new KeyModifierPropertyCodec());
            addPropertyCodec("object[]", new ArrayPropertyCodec(new DefaultPropertyCodec()));
            addPropertyCodec("color", new ColorPropertyCodec());
            addPropertyCodec("color[]", new ArrayPropertyCodec(new ColorPropertyCodec()));
            addPropertyCodec("automationObject", new AutomationObjectPropertyCodec());
            addPropertyCodec("automationObject[]", new ArrayPropertyCodec(new AutomationObjectPropertyCodec()));
            addPropertyCodec("asset", new AssetPropertyCodec());
            addPropertyCodec("asset[]", new ArrayPropertyCodec(new AssetPropertyCodec()));
            addPropertyCodec("listDataObject", new ListDataObjectCodec());
            addPropertyCodec("listDataObject[]", new ArrayPropertyCodec(new ListDataObjectCodec()));
            addPropertyCodec("dateRange", new DateRangePropertyCodec());
            addPropertyCodec("dateObject", new DatePropertyCodec());
            addPropertyCodec("dateRange[]", new ArrayPropertyCodec(new DateRangePropertyCodec()));
            addPropertyCodec("event", new TriggerEventPropertyCodec());
            addPropertyCodec("tab", new TabObjectCodec());
            addPropertyCodec("scrollDetail", new ScrollDetailPropertyCodec());
            addPropertyCodec("dateScrollDetail", new DateScrollDetailPropertyCodec());
            addPropertyCodec("scrollDirection", new ScrollDirectionPropertyCodec());
            try {
                var codecObj:Object = getDefinitionByName("codec.HitDataCodec");
                addPropertyCodec("hitDataCodec[]", new ArrayPropertyCodec(new codecObj()));
            } catch(e:Error) {
            }

            isInitailized = true;
        }
    }

    private function addPropertyCodec(codecName:String, codec:IAutomationPropertyCodec):void {
        propertyCodecMap[codecName] = codec;
    }

    public function getPropertyCodecByName(codecName:String):IAutomationPropertyCodec {
        return propertyCodecMap[codecName];
    }

    public function encodeProperties(obj:Object, propertyDescriptors:Array, interactionReplayer:IAutomationObject):Array {
        var result:Array = [];
        var consecutiveDefaultValueCount:Number = 0;
        for (var i:int = 0; i < propertyDescriptors.length; i++) {
            trace(" obj :: [" + obj + "] property descriptor [" + (propertyDescriptors[i] as SeleFxPropertyDescriptor).name + "]");
            var val:Object = getPropertyValue(obj, propertyDescriptors[i], interactionReplayer);
            trace("\t Encoded Value [" + val + "]")
            var isDefaultValueNull:Boolean = propertyDescriptors[i].defaultValue == "null";

            consecutiveDefaultValueCount = (!(val == null && isDefaultValueNull) &&
                                            (propertyDescriptors[i].defaultValue == null ||
                                             val == null ||
                                             propertyDescriptors[i].defaultValue != val.toString())
                    ? 0
                    : consecutiveDefaultValueCount + 1);

            result.push(val);
        }

        result.splice(result.length - consecutiveDefaultValueCount,
                consecutiveDefaultValueCount);
        return result;
    }

    public function encodeValue(value:Object, testingToolType:String, codecName:String, relativeParent:IAutomationObject):Object {
        var pd:SeleFxPropertyDescriptor =
                new SeleFxPropertyDescriptor("value",
                        false,
                        false,
                        testingToolType,
                        codecName);
        var obj:Object = {value:value};
        return getPropertyValue(obj, pd, relativeParent);
    }

    public function decodeProperties(obj:Object, args:Array, propertyDescriptors:Array, interactionReplayer:IAutomationObject):void {
        for (var i:int = 0; i < propertyDescriptors.length; i++) {

            if (args != null &&
                i < args.length &&
                args[i] == "null" &&
                propertyDescriptors[i].defaultValue == "null")
                args[i] = null;
            if (args != null &&
                i < args.length &&
                ((args[i] != null && args[i] != "") || propertyDescriptors[i].defaultValue == null))
                setPropertyValue(obj,
                        args[i],
                        propertyDescriptors[i],
                        interactionReplayer);
            else if (propertyDescriptors[i].defaultValue != null)
                setPropertyValue(obj,
                        (propertyDescriptors[i].defaultValue == "null"
                                ? null
                                : propertyDescriptors[i].defaultValue),
                        propertyDescriptors[i],
                        interactionReplayer);
            else
                throw new Error("Missing Argument " + propertyDescriptors[i].name);
        }
    }

    public function getPropertyValue(obj:Object, pd:SeleFxPropertyDescriptor, relativeParent:IAutomationObject = null):Object {
        var coder:IAutomationPropertyCodec = propertyCodecMap[pd.codecName];

        if (coder == null)
            coder = propertyCodecMap["object"];

        if (relativeParent == null)
            relativeParent = obj as IAutomationObject;

        trace("\t En or DeCoder.." + coder);

        return coder.encode(automationManager, obj, pd, relativeParent);
    }

    public function get automationManager():IAutomationManager {
        return Automation.automationManager;
    }

    public function setPropertyValue(obj:Object,
                                     value:Object,
                                     pd:SeleFxPropertyDescriptor,
                                     relativeParent:IAutomationObject = null):void {
        var coder:IAutomationPropertyCodec = propertyCodecMap[pd.codecName];

        if (coder == null)
            coder = propertyCodecMap["object"];

        if (relativeParent == null)
            relativeParent = obj as IAutomationObject;

        coder.decode(automationManager, obj, value, pd, relativeParent);
    }

}
}